package cn.test.processor;

import cn.test.proxy.HelloWorldHandler;
import cn.test.proxy.HelloWorldImpl;
import cn.test.service.HelloWorld;
import javassist.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @auther hanchao
 * @date 2016/11/11 12:19
 */
@Component
public class AllProcessor implements BeanPostProcessor,BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor,BeanFactoryAware,ApplicationListener<ContextRefreshedEvent> {

    private BeanDefinitionRegistry registry;
    //this.beanFactory.getInstantiationStrategy().instantiate
    private BeanFactory beanFactory;


    //最早,用于获取beanfactory
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("setBeanFactory"+beanFactory);
    }

    //最早,仅次于上面的
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        this.registry = beanDefinitionRegistry;
        System.out.println("postProcessBeanDefinitionRegistry" + registry);
    }

    //在bean定义完之后
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        System.out.println("postProcessBeanFactory");

        System.out.println(configurableListableBeanFactory.getBean(UserDao.class));

        HelloWorld helloWorld=new HelloWorldImpl();
        HelloWorld helloProxy = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(), helloWorld.getClass().getInterfaces(), new HelloWorldHandler(helloWorld));

        /*RootBeanDefinition definition1 = new RootBeanDefinition();
        definition1.setBeanClass(HelloWorldImpl.class);
        definition1.setSource(helloProxy);
        registry.registerBeanDefinition("asdfascxcv",definition1);*/


//        RootBeanDefinition definition1 = new RootBeanDefinition();
//        definition1.setBeanClass(FactoryBeanclass.class);
//        definition1.setAttribute("targetClass",HelloWorld.class);
//        definition1.setAttribute("target",helloProxy);
//        definition1.setLazyInit(false);
        //definition1.setAttribute();
        BeanDefinition definition1 = BeanDefinitionBuilder.rootBeanDefinition(FactoryBeanclass.class).addPropertyValue("targetClass",HelloWorld.class).addPropertyValue("target",helloProxy).getBeanDefinition();
        registry.registerBeanDefinition("asdfascxcv",definition1);
    }

    //实例化bean,注入属性之后,initlize之前
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        //System.out.println("postProcessBeforeInitialization");
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
       // System.out.println("postProcessAfterInitialization");
        return o;
    }


    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("最后执行的");
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctInterface = pool.get("cn.test.service.HelloWorld");
            CtClass cc = pool.makeClass("cn.test.service.HelloServiceImpl");
            cc.addInterface(ctInterface);
            Method[] methods = HelloWorld.class.getMethods();
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
                dynamicImplementsMethodsFromInterface(cc,method);
            }

            Object obj = cc.toClass().newInstance();
            ((HelloWorld)obj).sayHello();


//            BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(obj.getClass()).getBeanDefinition();
//            registry.registerBeanDefinition("asdasdasd",definition);


            HelloWorld helloWorld=new HelloWorldImpl();
            helloWorld.sayHello();
            HelloWorld helloProxy = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(), helloWorld.getClass().getInterfaces(), new HelloWorldHandler(helloWorld));
            //可能还有helloProxy1,2,......n,可以这么单独拿到，也可以外部工厂类，根据.get(Interface.class)拿到，
            //怎么注册到spring




            ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
            scanner.addIncludeFilter(new AnnotationTypeFilter(SuppressWarnings.class));
            System.out.println(scanner.scan("cn.test.proxy"));
            //doscan不可见，


            System.out.println(event.getApplicationContext().getBean(HelloWorld.class));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }













    //#############################


    private static void dynamicImplementsMethodsFromInterface(CtClass implementer, Method method)
            throws CannotCompileException {
        String methodCode = generateCode( method);
//		System.out.println(methodCode);
        CtMethod cm = CtNewMethod.make(methodCode, implementer);
        implementer.addMethod(cm);
    }

    private static String generateCode(Method method) {
        String methodName = method.getName();
        String methodReturnType = method.getReturnType().getName();
        Class<?>[] parameters = method.getParameterTypes();
        Class<?>[] exceptionTypes = method.getExceptionTypes();
        StringBuffer exceptionBuffer = new StringBuffer();

        // 异常
        if (exceptionTypes.length > 0)
            exceptionBuffer.append(" throws ");
        for (int i = 0; i < exceptionTypes.length; i++) {
            if (i != exceptionTypes.length - 1)
                exceptionBuffer.append(exceptionTypes[i].getName()).append(",");
            else
                exceptionBuffer.append(exceptionTypes[i].getName());
        }

        StringBuffer parameterBuffer = new StringBuffer();
        // 组装方法的参数列表
        for (int i = 0; i < parameters.length; i++) {
            Class<?> parameter = parameters[i];
            String parameterType = parameter.getName();
            // 动态指定方法参数的变量名
            String refName = "a" + i;
            if (i != parameters.length - 1)
                parameterBuffer.append(parameterType).append(" " + refName)
                        .append(",");
            else
                parameterBuffer.append(parameterType).append(" " + refName);
        }

        StringBuffer methodDeclare = new StringBuffer();
        // 方法声明，由于是实现接口的方法，所以是public
        methodDeclare.append("public ").append(methodReturnType).append(" ")
                .append(methodName).append("(").append(parameterBuffer)
                .append(")").append(exceptionBuffer).append(" {\n");

        if (method.getReturnType().isPrimitive()) {
            if (method.getReturnType().equals(Void.TYPE)) {
                methodDeclare.append("System.err.println(\"hello world\");\n");
                methodDeclare.append("return ;\n");
            } else if (method.getReturnType().equals(Boolean.TYPE))
                methodDeclare
                        .append("return false;\n");
            else if (method.getReturnType().equals(Integer.TYPE))
                methodDeclare
                        .append("return -1;\n");
            else if (method.getReturnType().equals(Long.TYPE))
                methodDeclare.append("return -1L;\n");
            else if (method.getReturnType().equals(Float.TYPE))
                methodDeclare
                        .append("return 0f;\n");
            else if (method.getReturnType().equals(Double.TYPE))
                methodDeclare
                        .append("return 0d;\n");
            else if (method.getReturnType().equals(Character.TYPE))
                methodDeclare
                        .append("return 'a';\n");
            else if (method.getReturnType().equals(Byte.TYPE))
                methodDeclare.append("return ((Byte)\"a\").byteValue();\n");
            else if (method.getReturnType().equals(Short.TYPE))
                methodDeclare
                        .append("return 0;\n");
            else{
                methodDeclare.append("return null;\n");
            }
        } else {
            methodDeclare.append("return null;\n");
        }

        methodDeclare.append("}");

        return methodDeclare.toString();
    }
}
